#include "objectModelRenderer.h"
#include "model/objectModel/textureLoader.h"
#include "model/matrix4x4.h"

ObjectModelRenderer::ObjectModelRenderer() {
}

ObjectModelRenderer::~ObjectModelRenderer() {
}

void ObjectModelRenderer::render(ObjectModel& model,bool notex){

	if(!model.isDisplayListCreated()){
		unsigned int id = glGenLists(1);
		model.setDisplayListId(id);
		glNewList(id, GL_COMPILE);

		const std::vector<ModelGroup*>& groups = model.groups();
		for(std::vector<ModelGroup*>::const_iterator it=groups.begin();it!=groups.end();++it){
			glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,(float*)&(*it)->material().ambient());
			glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,(float*)&(*it)->material().diffuse());
			glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,(float*)&(*it)->material().specular());
			glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, (*it)->material().shininess());

			if((*it)->_material.textured() && !notex){
				glEnable(GL_TEXTURE_2D);
				glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
				glBindTexture(GL_TEXTURE_2D,getTexture((*it)->_material));
			}else{
				glDisable(GL_TEXTURE_2D);
			}

			glEnable(GL_NORMALIZE);
			glBegin(model.getType());
				for(unsigned int c=0;c<(*it)->faces().size();c+=3){
					glNormal3fv(&model.normals()[3*(*it)->faces()[c+2]]);
					if((*it)->_material.textured())
						glTexCoord2fv( &model.textures()[2*(*it)->faces()[c+1]]);
					glVertex3fv(	&model.vertices()[3*(*it)->faces()[c]]);
				}
			glEnd();
			glDisable(GL_NORMALIZE);
		}

		glEndList();
	}else{
		glCallList(model.getDisplayListId());
	}
}


unsigned int ObjectModelRenderer::getTexture(Material& mat){
	ImageCache::TexturePtr& tex = mat.texture();
	std::map<std::string,GLuint>::iterator it =  textures.find(tex->name());
	unsigned int id=0;

	if(it==textures.end()){
		TextureLoader* loader;
		if(!tex->loader()){
			loader = new TextureLoader();
			tex->loader(loader);
		}else{
			loader = tex->loader();
		}

		loader->load(*tex);
		id = loader->id();

		textures.insert( std::make_pair(tex->name(),id) );
	}else{
		id = it->second;
	}

	return id;
}

void ObjectModelRenderer::addShadowProyection(const Point& planeNormal,const Point& planePoint,const Point& lightPos){
	GLfloat shadowMat[16];
	GLfloat dot = planeNormal*lightPos;
	GLfloat c = planePoint*planeNormal - dot;

	shadowMat[0]	= lightPos.x1*planeNormal.x1+c;
	shadowMat[4]	= planeNormal.x2*lightPos.x1;
	shadowMat[8]	= planeNormal.x3*lightPos.x1;
	shadowMat[12]	= -lightPos.x1*c-lightPos.x1*dot;

	shadowMat[1]	= planeNormal.x1*lightPos.x2;
	shadowMat[5]	= lightPos.x2*planeNormal.x2+c;
	shadowMat[9]	= planeNormal.x3*lightPos.x2;
	shadowMat[13]	= -lightPos.x2*c-lightPos.x2*dot;

	shadowMat[2]	= planeNormal.x1*lightPos.x3;
	shadowMat[6]	= planeNormal.x2*lightPos.x3;
	shadowMat[10]	= lightPos.x3*planeNormal.x3+c;
	shadowMat[14]	= -lightPos.x3*c-lightPos.x3*dot;

	shadowMat[3]	= planeNormal.x1;
	shadowMat[7]	= planeNormal.x2;
	shadowMat[11]	= planeNormal.x3;
	shadowMat[15]	= -dot;

    glMultMatrixf((GLfloat *) shadowMat);
}
